package com.example.account.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.account.dao.AccountMapper;
import com.example.account.dao.TccLogMapper;
import com.example.account.entity.Account;
import com.example.account.entity.TccLog;
import com.example.account.entity.TccStatus;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@RequiredArgsConstructor
public class AccountService implements IDeductMoney {

    private final AccountMapper accountMapper;
    private final TccLogMapper tccDao;

    @Override
    public void deduct(String userId, Integer money) {
        TccLog tccLog = null;

        // 1. 业务悬挂判断
        String xid = RootContext.getXID();
        tccLog = tccDao.selectOne(new LambdaQueryWrapper<TccLog>().eq(TccLog::getXid, xid));
        if (tccLog != null) {
            log.warn("出现了业务悬挂情况");
            tccLog.setActionName("[suspend]扣减金额" + money);
            tccLog.setStatus(TccStatus.SUSPENDED);
            tccDao.updateById(tccLog);
            return;
        }

        // 2. 业务逻辑
        Account account = accountMapper.selectOne(new LambdaQueryWrapper<Account>().eq(Account::getUserId, userId));
        account.setMoney(account.getMoney() - money);
        account.setFreezeMoney(account.getFreezeMoney() + money);
        accountMapper.updateById(account);

        // 3. TCC 日志
        tccLog = TccLog.builder()
                .xid(xid)
                .actionName("[try]扣减金额" + money)
                .status(TccStatus.TRIED)
                .build();
        tccDao.insert(tccLog);
    }

    @Override
    public boolean deductCancel(BusinessActionContext context) {
        String userId = (String) context.getActionContext("userId");
        Integer money = (Integer) context.getActionContext("money");

        // 空回滚业务判断
        TccLog tccLog;

        String xid = context.getXid();
        tccLog = tccDao.selectOne(new LambdaQueryWrapper<TccLog>().eq(TccLog::getXid, xid));
        if (tccLog == null) {
            log.warn("未执行 TRY 就执行了 Cancel ！");
            tccLog = TccLog.builder()
                    .xid(xid)
                    .actionName("[cancel]扣减金额" + money)
                    .status(TccStatus.ROLLBACKED)
                    .build();
            tccDao.insert(tccLog);
            return true;
        }

        // 幂等判断
        if (tccLog.getStatus().equals(TccStatus.ROLLBACKED)) {
            log.info("幂等判断：重复 CANCEL");
            return true;
        }

        // 业务逻辑回滚
        Account account = accountMapper.selectOne(new LambdaQueryWrapper<Account>().eq(Account::getUserId, userId));
        account.setMoney(account.getMoney() + money);
        account.setFreezeMoney(account.getFreezeMoney() - money);
        accountMapper.updateById(account);

        // 更新 TCC 日志
        tccLog.setActionName("[cancel]扣减金额" + money);
        tccLog.setStatus(TccStatus.ROLLBACKED);
        tccDao.updateById(tccLog);

        return true;
    }

    @Override
    public boolean deductCommit(BusinessActionContext context) {
        String userId = (String) context.getActionContext("userId");
        Integer money = (Integer) context.getActionContext("money");

        TccLog tccLog;

        String xid = context.getXid();
        tccLog = tccDao.selectOne(new LambdaQueryWrapper<TccLog>().eq(TccLog::getXid, xid));
        if (tccLog == null)
            throw new RuntimeException("业务逻辑错误：Try 没执行却先执行了 Commit");

        // 幂等判断
        if (tccLog.getStatus().equals(TccStatus.COMMITTED)) {
            log.info("幂等判断：重复 Commit");
            return true;
        }

        // 提交逻辑
        Account account = accountMapper.selectOne(new LambdaQueryWrapper<Account>().eq(Account::getUserId, userId));
        account.setFreezeMoney(account.getFreezeMoney() - money);
        accountMapper.updateById(account);

        // 更新日志
        tccLog.setActionName("[commit] 扣减金额");
        tccLog.setStatus(TccStatus.COMMITTED);
        tccDao.updateById(tccLog);

        return true;
    }
}
